/*
 * Copyright 2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * 龙芯3A6000 LS7A2000桥片 APIC中断控制器驱动
 * 参考Linux的loongson_pch_pic驱动实现
 */

#include "loongson_pch_pic.h"

#include <KernelExport.h>
#include <PCI.h>
#include <SupportDefs.h>
#include <arch/cpu.h>
#include <arch/loongarch64/arch_int.h>
#include <util/kernel_cpp.h>

#define TRACE_LOONGSON_PCH_PIC
#ifdef TRACE_LOONGSON_PCH_PIC
#define TRACE(x...) dprintf("loongson_pch_pic: " x)
#else
#define TRACE(x...)
#endif

#define ERROR(x...) dprintf("loongson_pch_pic: " x)

// 全局中断控制器实例
static loongson_pch_pic *sPCHPIC = NULL;

// 寄存器读写函数
static inline uint32
read_pch_pic_reg(addr_t base, uint32 reg)
{
    return *(volatile uint32 *)(base + reg);
}

static inline void
write_pch_pic_reg(addr_t base, uint32 reg, uint32 val)
{
    *(volatile uint32 *)(base + reg) = val;
}

// 中断控制器初始化
status_t
loongson_pch_pic_init(addr_t base_addr, uint32 irq_base)
{
    TRACE("Initializing LS7A2000 PCH PIC at %#" B_PRIxADDR "\n", base_addr);

    // 分配中断控制器结构体
    sPCHPIC = new (std::nothrow) loongson_pch_pic;
    if (sPCHPIC == NULL)
    {
        ERROR("Failed to allocate loongson_pch_pic structure\n");
        return B_NO_MEMORY;
    }

    // 初始化结构体
    memset(sPCHPIC, 0, sizeof(loongson_pch_pic));
    sPCHPIC->base_addr = base_addr;
    sPCHPIC->irq_base = irq_base;
    sPCHPIC->num_irqs = LS7A_PCH_PIC_MAX_IRQS;
    sPCHPIC->route_mode = LS7A_PCH_PIC_ROUTE_HTMSI; // 默认使用HTMSI路由模式

    // 禁用所有中断
    for (uint32 i = 0; i < sPCHPIC->num_irqs / 32; i++)
    {
        write_pch_pic_reg(base_addr, LS7A_PCH_PIC_INT_MASK_BASE + i * 4, 0xFFFFFFFF);
        sPCHPIC->irq_mask[i] = 0xFFFFFFFF;
    }

    // 清除所有中断状态
    for (uint32 i = 0; i < sPCHPIC->num_irqs / 32; i++)
    {
        write_pch_pic_reg(base_addr, LS7A_PCH_PIC_INT_CLEAR_BASE + i * 4, 0xFFFFFFFF);
    }

    // 配置中断路由
    for (uint32 i = 0; i < sPCHPIC->num_irqs; i++)
    {
        uint32 reg = LS7A_PCH_PIC_INT_ROUTE_BASE + i * 4;
        write_pch_pic_reg(base_addr, reg, sPCHPIC->route_mode);
    }

    // 配置中断向量
    if (sPCHPIC->route_mode == LS7A_PCH_PIC_ROUTE_HTMSI)
    {
        for (uint32 i = 0; i < sPCHPIC->num_irqs; i++)
        {
            uint32 reg = LS7A_PCH_PIC_INT_HTMSI_VEC_BASE + i * 4;
            // 设置中断向量为基础IRQ号 + 中断索引
            write_pch_pic_reg(base_addr, reg, sPCHPIC->irq_base + i);
        }
    }

    TRACE("LS7A2000 PCH PIC initialized successfully\n");
    return B_OK;
}

// 中断控制器卸载
void loongson_pch_pic_uninit(void)
{
    if (sPCHPIC != NULL)
    {
        // 禁用所有中断
        for (uint32 i = 0; i < sPCHPIC->num_irqs / 32; i++)
        {
            write_pch_pic_reg(sPCHPIC->base_addr, LS7A_PCH_PIC_INT_MASK_BASE + i * 4, 0xFFFFFFFF);
        }

        delete sPCHPIC;
        sPCHPIC = NULL;
    }
}

// 启用IO中断
status_t
loongson_pch_pic_enable_io_interrupt(uint32 irq)
{
    if (sPCHPIC == NULL)
        return B_NO_INIT;

    if (irq >= sPCHPIC->num_irqs)
        return B_BAD_VALUE;

    uint32 reg_index = irq / 32;
    uint32 bit_index = irq % 32;

    // 清除中断掩码中的对应位
    sPCHPIC->irq_mask[reg_index] &= ~(1 << bit_index);

    // 更新硬件寄存器
    write_pch_pic_reg(sPCHPIC->base_addr,
                      LS7A_PCH_PIC_INT_MASK_BASE + reg_index * 4,
                      sPCHPIC->irq_mask[reg_index]);

    TRACE("Enabled IO interrupt %lu\n", irq);
    return B_OK;
}

// 禁用IO中断
status_t
loongson_pch_pic_disable_io_interrupt(uint32 irq)
{
    if (sPCHPIC == NULL)
        return B_NO_INIT;

    if (irq >= sPCHPIC->num_irqs)
        return B_BAD_VALUE;

    uint32 reg_index = irq / 32;
    uint32 bit_index = irq % 32;

    // 设置中断掩码中的对应位
    sPCHPIC->irq_mask[reg_index] |= (1 << bit_index);

    // 更新硬件寄存器
    write_pch_pic_reg(sPCHPIC->base_addr,
                      LS7A_PCH_PIC_INT_MASK_BASE + reg_index * 4,
                      sPCHPIC->irq_mask[reg_index]);

    TRACE("Disabled IO interrupt %lu\n", irq);
    return B_OK;
}

// 配置IO中断
status_t
loongson_pch_pic_configure_io_interrupt(uint32 irq, uint32 config)
{
    if (sPCHPIC == NULL)
        return B_NO_INIT;

    if (irq >= sPCHPIC->num_irqs)
        return B_BAD_VALUE;

    uint32 reg_index = irq / 32;
    uint32 bit_index = irq % 32;
    uint32 reg_val;

    // 配置触发类型（边沿/电平）
    reg_val = read_pch_pic_reg(sPCHPIC->base_addr, LS7A_PCH_PIC_INT_EDGE_BASE + reg_index * 4);
    if (config & LS7A_INT_EDGE_TRIGGER)
        reg_val |= (1 << bit_index); // 边沿触发
    else
        reg_val &= ~(1 << bit_index); // 电平触发
    write_pch_pic_reg(sPCHPIC->base_addr, LS7A_PCH_PIC_INT_EDGE_BASE + reg_index * 4, reg_val);

    // 配置极性（高电平/低电平有效）
    reg_val = read_pch_pic_reg(sPCHPIC->base_addr, LS7A_PCH_PIC_INT_POL_BASE + reg_index * 4);
    if (config & LS7A_INT_ACTIVE_LOW)
        reg_val |= (1 << bit_index); // 低电平有效
    else
        reg_val &= ~(1 << bit_index); // 高电平有效
    write_pch_pic_reg(sPCHPIC->base_addr, LS7A_PCH_PIC_INT_POL_BASE + reg_index * 4, reg_val);

    TRACE("Configured IO interrupt %lu with config %#lx\n", irq, config);
    return B_OK;
}

// 中断处理函数
int32 loongson_pch_pic_handle_interrupt(void *data)
{
    if (sPCHPIC == NULL)
        return B_UNHANDLED_INTERRUPT;

    int32 handled = B_UNHANDLED_INTERRUPT;

    // 检查每个中断状态寄存器
    for (uint32 i = 0; i < sPCHPIC->num_irqs / 32; i++)
    {
        uint32 status = read_pch_pic_reg(sPCHPIC->base_addr, LS7A_PCH_PIC_INT_STATUS_BASE + i * 4);
        uint32 mask = sPCHPIC->irq_mask[i];

        // 只处理未被屏蔽的中断
        uint32 active = status & ~mask;

        if (active)
        {
            // 找出激活的中断
            for (uint32 j = 0; j < 32; j++)
            {
                if (active & (1 << j))
                {
                    uint32 irq = i * 32 + j;
                    uint32 global_irq = sPCHPIC->irq_base + irq;

                    TRACE("Handling interrupt %lu (global %lu)\n", irq, global_irq);

                    // 清除边沿触发中断
                    uint32 edge = read_pch_pic_reg(sPCHPIC->base_addr,
                                                   LS7A_PCH_PIC_INT_EDGE_BASE + i * 4);
                    if (edge & (1 << j))
                    {
                        write_pch_pic_reg(sPCHPIC->base_addr,
                                          LS7A_PCH_PIC_INT_CLEAR_BASE + i * 4,
                                          1 << j);
                    }

                    // 调用系统中断处理函数
                    int_io_interrupt_handler(global_irq);
                    handled = B_HANDLED_INTERRUPT;
                }
            }
        }
    }

    return handled;
}